<?php
namespace PsiFramework\Applications\DefaultApp\Classes\DAO;

use PsiFramework\Classes\Util\Database\Filters\QueryFilter;

use PsiFramework\Classes\Util\Security\Tools\Encryption;

use PsiFramework\Classes\Factory\DAOFactory;

use PsiFramework\Classes\Util\Manager\ApplicationManager;

use PsiFramework\Classes\Util\Manager\ParametersManager;

use PsiFramework\Classes\Util\Object\EMail;

import("Classes.DAO.AbstractDAO");
import("Applications.DefaultApp.Classes.Model.User");
import("Classes.Util.Object.Date");

use \PsiFramework\Classes\DAO\AbstractDAO;
use \PsiFramework\Classes\Util\Parameters\ObjectParameters;
use \PsiFramework\Classes\Util\Object\Date;
use \PsiFramework\Classes\Util\Database\Builder\QueryBuilder;
use \PsiFramework\Classes\Util\Database\Query\SqlColumn;
use \PsiFramework\Classes\Util\Database\Query\SqlFunction;
use \PsiFramework\Classes\Util\Database\Query\SqlOperation;
use \PsiFramework\Classes\Util\Database\Query\SqlTable;
use \PsiFramework\Classes\Util\Database\Query\SqlValue;
use \PsiFramework\Applications\DefaultApp\Classes\Model\User;

/**
 * PsiFramework
 * Php Simple Framework
 *
 * @author Rémi San
 * @version beta
 */

/**
 * The UserDAO is the Data Access Object for the class User
 *
 * @package DAO
 */
class UserDAO extends AbstractDAO
{

    protected static $_singleton = null;

    /**
     * Retrieve the user who corresponds to the specified login and password
     *
     * @param     string    $login       the user login
     * @param     string    $password    the user password
     * @return    User      the retrieved user
     */
    public function retrieveByLoginPassword(
        /*string*/ $login,
        /*string*/ $password
    )
    {

        $queryBuilder = new QueryBuilder();
        $queryBuilder->createSelectQuery();

        $table = new SqlTable("users", "psiframework");
        $queryBuilder->setTable($table);

        $columnlogin = new SqlColumn("user_login", $table);
        $columnEmail = new SqlColumn("user_email", $table);
        $columnPassword = new SqlColumn("user_password", $table);

        $and = new SqlOperation(SqlOperation::$and);

        $or = new SqlOperation(SqlOperation::$or);

        $equalLogin = new SqlOperation(SqlOperation::$equal);
        $equalLogin->addParameter($columnlogin);
        $equalLogin->addParameter(new SqlValue($login));

        $equalEmail = new SqlOperation(SqlOperation::$equal);
        $equalEmail->addParameter($columnEmail);
        $equalEmail->addParameter(new SqlValue($login));

        $or->addParameter($equalLogin);
        $or->addParameter($equalEmail);

        $equalPassword = new SqlOperation(SqlOperation::$equal);
        $equalPassword->addParameter($columnPassword);
        $equalPassword->addParameter(new SqlValue($password));

        $and->addParameter($or);
        $and->addParameter($equalPassword);

        $queryBuilder->addWhereClause($and);

        $query = $queryBuilder->buildQuery();

        $result = $this->_database->executeQuery($query);
        $user = null ;
        if ($result) {
            $obj = $this->_database->getNextResult($result);
            if ($obj) {
                $user = $this->getObjectFromSqlObject($obj, false, false, false);
            }
        }
        return $user;
    }
    
    /**
     * Retrieve the user who corresponds to the specified login or email
     *
     * @param     string    $login       the user login or email
     * 
     * @return    User      the retrieved user
     */
    public function retrieveByLoginOrEmail(/*string*/ $login)
    {
    
        $queryBuilder = new QueryBuilder();
        $queryBuilder->createSelectQuery();
    
        $table = new SqlTable("users", "psiframework");
        $queryBuilder->setTable($table);
    
        $columnlogin = new SqlColumn("user_login", $table);
        $columnEmail = new SqlColumn("user_email", $table);
    
        $or = new SqlOperation(SqlOperation::$or);
    
        $equalLogin = new SqlOperation(SqlOperation::$equal);
        $equalLogin->addParameter($columnlogin);
        $equalLogin->addParameter(new SqlValue($login));
    
        $equalEmail = new SqlOperation(SqlOperation::$equal);
        $equalEmail->addParameter($columnEmail);
        $equalEmail->addParameter(new SqlValue($login));
    
        $or->addParameter($equalLogin);
        $or->addParameter($equalEmail);
    
        $queryBuilder->addWhereClause($or);
    
        $query = $queryBuilder->buildQuery();
    
        $result = $this->_database->executeQuery($query);
        $user = null ;
        if ($result) {
            $obj = $this->_database->getNextResult($result);
            if ($obj) {
                $user = $this->getObjectFromSqlObject($obj);
            }
        }
        return $user;
    }

    protected function retrieveByValue(
        /*string*/ $colName,
        /*string*/ $value,
        /*int*/    $userId
    )
    {

        $queryBuilder = new QueryBuilder();
        $queryBuilder->createSelectQuery();

        $table = new SqlTable("users", "psiframework");
        $queryBuilder->setTable($table);

        $columnValue = new SqlColumn($colName, $table);
        $columnId = new SqlColumn("user_id", $table);

        $and = new SqlOperation(SqlOperation::$and);

        $equalValue = new SqlOperation(SqlOperation::$equal);
        $equalValue->addParameter($columnValue);
        $equalValue->addParameter(new SqlValue($value));
        $and->addParameter($equalValue);

        if (!isNullOrEmptyString($userId)) {
            $equalId = new SqlOperation(SqlOperation::$different);
            $equalId->addParameter($columnId);
            $equalId->addParameter(new SqlValue($userId));

            $and->addParameter($equalId);
        }

        $queryBuilder->addWhereClause($and);

        $query = $queryBuilder->buildQuery();

        $result = $this->_database->executeQuery($query);
        $user = null ;
        if ($result) {
            $obj = $this->_database->getNextResult($result);
            if ($obj) {
                $user = $this->getObjectFromSqlObject($obj);
            }
        }

        return $user;
    }

    /**
     * Retrieve the user who corresponds to the specified login and password
     *
     * @param     string    $login        the user login
     * @param     int        $userId        the user id
     * @return     User    the retrieved user
     */
    public function retrieveByLogin(/*string*/ $login, /*int*/ $userId)
    {
        return $this->retrieveByValue("user_login", $login, $userId);
    }

    /**
     * Retrieve the user who corresponds to the specified login and password
     *
     * @param     string    $email        the user email
     * @param     int        $userId        the user id
     * @return     User    the retrieved user
     */
    public function retrieveByEmail(/*string*/ $email, /*int*/ $userId)
    {
        return $this->retrieveByValue("user_email", $email, $userId);
    }

    /**
     * 
     * @param string $partialName
     * 
     * @return array
     */
    public function retrieveByPartialName(/*string*/ $partialName)
    {
        $value = '%' ;
        if (!isNullOrEmptyString($partialName)) {
            $value .= mb_strtoupper($partialName, 'utf-8') . "%" ;
        }
        $sqlValue = new SqlValue($value);
        
        //Columns
        
        $colLogin = new SqlColumn("user_login");
        $upperLogin = new SqlFunction("UPPER");
        $upperLogin->addParameter($colLogin);
    
        $colName = new SqlColumn("user_surname");
        $upperName = new SqlFunction("UPPER");
        $upperName->addParameter($colName);
        
        $colFirstName = new SqlColumn("user_surname");
        $upperFirstName = new SqlFunction("UPPER");
        $upperFirstName->addParameter($colFirstName);
        
        //Operations
        
        $likeLogin = new SqlOperation(SqlOperation::$like);
        $likeLogin->addParameter($upperLogin);
        $likeLogin->addParameter($sqlValue);
        
        $likeName = new SqlOperation(SqlOperation::$like);
        $likeName->addParameter($upperName);
        $likeName->addParameter($sqlValue);
        
        $likeFirstName = new SqlOperation(SqlOperation::$like);
        $likeFirstName->addParameter($upperFirstName);
        $likeFirstName->addParameter($sqlValue);
        
        //OR
        
        $or = new SqlOperation(SqlOperation::$or);
        $or->addParameter($likeLogin);
        $or->addParameter($likeName);
        $or->addParameter($likeFirstName);
    
        return $this->retrieveAll(new QueryFilter($or));
    }
    
    /**
     * Sends an email to the user with its new password
     * 
     * @param $user IUser
     * 
     * @return boolean
     */
    public function sendLostPasswordEmail(/*Object*/ $user)
    {
        $decryptedPwd = uniqid();
        $pwd = Encryption::encrypt($decryptedPwd);
        
        $user->setPassword($pwd);
        $ok = $this->save($user, true, false);
        
        if ($ok) {
            $paramManager = ParametersManager::getInstance();
            $applicationsParameters = $paramManager->getApplicationsParameters();
        
            $contact = $applicationsParameters->getContact();
            $subject = 'Inscription' ;
            $message = $decryptedPwd ;
        
            $application = ApplicationManager::getInstance()->getApplication();
            $appConfigDao = DAOFactory::getDAO('ApplicationConfiguration');
            $appConfig = $appConfigDao->retrieveByName($application->getName(), false);
        
            if (!is_null($appConfig)) {
                $contact = $appConfig->getParameters()->get('email');
                $page = $appConfig->getParameters()->get('new_password_message')->getObject(false, false);
                if (!is_null($page)) {
                    $subject = $page->getTitle();
                    $message = str_replace('<password>', $decryptedPwd, $page->getContent());
                }
            }
            
            $email = new EMail($contact, $user->getEmail(), $subject, $message);
            //send email to the user
            $email->send();
        }
        return $ok;
    }
    
    /**
     * Function to be called instead of the constructor.
     *
     * @return UserDAO    instance
     */
    public static function getInstance(ObjectParameters $objectParameters)
    {

        if (self::$_singleton==null) {
            self::$_singleton = new UserDAO($objectParameters);
        }

        return self::$_singleton;
    }

}
